import express from "express";
import bodyParser from "body-parser";
import { exec } from "child_process";
import path from "path";
import fs from "fs/promises";
import "./env.js";
const app = express();
const port = 1024; // 监听的端口号
let step = 1; // 记录步骤顺序的变量
app.use(bodyParser.json());
const projectPath = process.env.PROJECT_PATH ?? ""; // 项目路径
const destinationPath = process.env.DESTINATION_PATH ?? ""; // 目标路径，一般是nginx下的项目路径
const buildPath = process.env.BUILD_OUTPUT ?? ""; // 项目打包后输出路径，如dist，build等
const _log = (opts, ...args) => {
  const reset = `\x1b[0m`; // 重置样式
  const { type = "log" } = opts ?? {};
  let style = "";
  if (type === "error") {
    style = `\x1b[31m`; // 红色字体
  } else if (type === "log") {
    style = `\x1b[32m`; // 绿色字体
  }
  console[type](
    style,
    `[${type}] `,
    `[${new Date().toLocaleString()}]: \n`,
    ...args,
    reset
  );
};
// 执行命令的辅助函数，返回Promise以处理异步执行
const executeCommand = (command) => {
  return new Promise((resolve, reject) => {
    _log(null, `步骤 ${step}：执行命令 "${command}"`);
    exec(command, (error, stdout) => {
      if (error) {
        _log(
          { type: "error" },
          `步骤 ${step}：执行命令出错：${error}`,
          "[stdout]: ",
          stdout
        );
        reject(error);
      } else {
        _log(null, `步骤 ${step}：命令执行成功`);
        step++; // 执行成功，递增步骤数
        resolve(stdout);
      }
    });
  });
};

// 检查目标文件夹是否存在，如果不存在则创建，存在则清空
const ensureProjectFolder = async (projectPath) => {
  try {
    await fs.access(projectPath);
    const files = await fs.readdir(projectPath);
    for (const file of files) {
      const filePath = path.join(projectPath, file);
      const stat = await fs.stat(filePath);
      if (stat.isDirectory()) {
        await fs.rm(filePath, { recursive: true });
      } else {
        await fs.unlink(filePath);
      }
    }
  } catch (error) {
    // 如果文件夹不存在则创建
    await fs.mkdir(projectPath, { recursive: true });
  }
};

// 递归复制文件夹的函数
async function copyFolder(src, dest) {
  try {
    _log(null, `复制文件夹 "${src}" 到 "${dest}"`);
    await fs.mkdir(dest, { recursive: true });
    const files = await fs.readdir(src);
    for (const file of files) {
      const srcPath = path.join(src, file);
      const destPath = path.join(dest, file);
      const stats = await fs.stat(srcPath);
      if (stats.isDirectory()) {
        await copyFolder(srcPath, destPath);
      } else {
        await fs.copyFile(srcPath, destPath);
        _log(null, `文件 "${srcPath}" 复制到 "${destPath}"`);
      }
    }
    _log(null, `文件夹 "${src}" 成功复制到 "${dest}"`);
  } catch (error) {
    _log({ type: "error" }, "复制文件夹时出错:", error);
  }
}

app.post("/webhook", async (req, res) => {
  const payload = req.body;
  const head = req.headers;
  if (payload?.ref_type && head["x-gitea-event-type"] === "create") {
    _log(null, "从 Gitea 接收到Tag事件:", payload);
    try {
      await ensureProjectFolder(projectPath);
      const agreement = "http";
      const gitCommand = `cd ${projectPath} && git clone ${agreement}://${
        process.env.GIT_USER_NAME
      }:${process.env.GIT_PASS_WORD}@${payload.repository.clone_url.replace(
        `${agreement}://`,
        ""
      )} ./`;
      await executeCommand(gitCommand);
      await executeCommand(`cd ${projectPath} && git checkout ${payload.ref}`);
      const pnpmInstalled = await executeCommand("pnpm -v")
        .then(() => true)
        .catch(() => false);
      if (!pnpmInstalled) {
        await executeCommand("npm install -g pnpm");
      }
      await executeCommand(`cd ${projectPath} && pnpm install`);
      await executeCommand(`cd ${projectPath} && pnpm build`);
      await ensureProjectFolder(destinationPath);
      await copyFolder(path.join(projectPath, buildPath), destinationPath);
      _log(null, "部署成功");
      res.status(200).send("部署成功");
    } catch (error) {
      _log({ type: "error" }, "部署过程中出错:", error);
      res.status(500).send("部署失败");
    } finally {
      await ensureProjectFolder(projectPath);
      _log(null, `文件夹 "${projectPath}" 内容删除成功`);
    }
  } else {
    _log(null, "从 Gitea 接收到不可识别的事件");
    res.status(400).send("不可识别的事件");
  }
});

app.listen(port, () => {
  _log(null, `服务器正在端口 ${port} 上运行`);
});
